package view
{
    import flash.display.*;
    import flash.events.*;
    import flash.text.*;

    import model.GameModelLocator;
    import model.vo.GameVo;
    import view.*;

    public class CellMap extends Sprite
    {
        public static var ROW:int = 11;
        public static var COL:int = 11;
        public static var ERASABLE_LIMIT:int = 4;
        public static var STATE_NORMAL:int = 0;
        public static var STATE_SWAPPING:int = 1;
        public static var STATE_DROPPING:int = 2;
        public static var STATE_ERASING:int = 3;

        public static var STEP:int = 6;
        public static var DX0:Array = new Array( 0, -1,  0, 1, 1,  1);
        public static var DY0:Array = new Array(-1,  0,  1, 1, 0, -1);
        public static var DX1:Array = new Array(-1, -1, -1, 0, 1,  0);
        public static var DY1:Array = new Array(-1,  0,  1, 1, 0, -1);

        public var gameVo:GameVo;
        public var mission:Mission;
        private var map:Array;
        private var state:int;
        private var score:int;
        private var erasingCellNum:int;
        private var scalingCellNum:int;
        private var movingCellNum:int;
        private var comb:TextField;

        public function CellMap()
        {
            this.gameVo = GameModelLocator.getInstance().gameVo;
            this.mission = new Mission(0, -50);
            this.addChild(this.mission);
            this.x = 50;
            this.y = 90;
            this.state = CellMap.STATE_NORMAL;
            this.erasingCellNum = 0;
            this.scalingCellNum = 0;
            this.movingCellNum = 0;
            this.comb = new TextField();
            this.comb.x = -150;
            this.comb.y = 250;
            this.comb.text = this.score.toString();
            this.comb.textColor = 0xFF0000;
            this.comb.width = 50;
            this.comb.height = 30;
            this.comb.mouseEnabled = false;
            addChild(comb);

            this.map = new Array();
            for(var row:int=0; row<CellMap.ROW; row++)
            {
                var l:int = 0;
                if((row&1) == 1)
                {
                    l = CellMap.COL + 1;
                }
                else
                {
                    l = CellMap.COL;
                }

                var array:Array = new Array();
                for(var col:int=0; col<l; col++)
                {
                    var cell:Cell;
                    if((row&1) == 1)
                    {
                        cell = new Cell(Cell.TYPE_EMPTY,
                                        col, row,
                                        Cell.WIDTH*col,
                                        Cell.HEIGHT*row*0.75);
                    }
                    else
                    {
                        cell = new Cell(Cell.TYPE_EMPTY,
                                        col, row,
                                        Cell.WIDTH*(col+0.5),
                                        Cell.HEIGHT*row*0.75);
                    }
                    array.push(cell);
                    this.addChild(cell);
                }
                this.map.push(array);
            }
            this.addEventListener(MouseEvent.MOUSE_OVER, this.mouseOver);
            this.addEventListener(MouseEvent.MOUSE_OUT, this.mouseOut);
            Cell.addMovedListener(Cell.CELL_MOVED, this.cellMoved);
            Cell.addMovedListener(Cell.CELL_ERASED, this.cellErased);
        }

        public function reset():void
        {
            //trace("Log: CellMap.reset()");
            this.state = CellMap.STATE_NORMAL;
            this.score = 0;
            this.comb.text = this.score.toString();
            this.erasingCellNum = 0;
            this.scalingCellNum = 0;
            this.movingCellNum = 0;
            Cell.LOCKED_ALL = false;
            Cell.SELECTED_CELL = null;
            Cell.OVERED_CELL = null;
            Cell.TYPE_NUM = this.gameVo.cellTypeRules[this.gameVo.level - 1];
            for(var row:int=0; row<this.map.length; row++)
            {
                for(var col:int=0; col<this.map[row].length; col++)
                {
                    this.map[row][col].reset();
                    this.map[row][col].type = Cell.TYPE_NUM * Math.random();
                }
            }
            this.checkMap();
            this.mission.setMissionRules(this.gameVo.missionRules);
        }

        public function checkMap():void
        {
            for(var row:int=0; row<this.map.length; row++)
            {
                for(var col:int=0; col<this.map[row].length; col++)
                {
                    while(this.isErasable(this.map[row][col]))
                    {
                        this.map[row][col].type = Cell.TYPE_NUM * Math.random();
                    }
                }
            }
        }

// Display
        public function display():void
        {
            this.displayCells();
        }

        public function displayCells():void
        {
            for(var row:int=0; row<this.map.length; row++)
            {
                for(var col:int=0; col<this.map[row].length; col++)
                {
                    this.map[row][col].display();
                }
            }
        }

// GetFunction
        public function getCell(col:int, row:int):Cell
        {
            if(row >= CellMap.ROW || row < 0) return null;
            if((row&1) == 1)
            {
                if(col >= CellMap.COL + 1 || col < 0) return null;
            }
            else
            {
                if(col >= CellMap.COL || col < 0) return null;
            }
            return this.map[row][col];
        }

// IsFunction
        public function isAdjacent(a:Cell, b:Cell):Boolean
        {
            if((a == null) || (b == null)) return false;
            for(var i:int=0; i<CellMap.STEP; i++)
            {
                if((a.row&1) == 0)
                {
                    if(((a.col + CellMap.DX0[i]) == b.col) &&
                       ((a.row + CellMap.DY0[i]) == b.row))
                    {
                        return true;
                    }
                }
                else
                {
                    if(((a.col + CellMap.DX1[i]) == b.col) &&
                       ((a.row + CellMap.DY1[i]) == b.row))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        public function isErasable(a:Cell):Boolean
        {
            if(a == null) return false;
            for(var i:int=0; i<CellMap.STEP/2; i++)
            {
                var col:int = a.col;
                var row:int = a.row;
                var sum:int = 0;
                var cell:Cell = this.getCell(col, row);
                while((cell != null) && (cell.type == a.type))
                {
                    sum ++;
                    if((row&1) == 0)
                    {
                        col += CellMap.DX0[i];
                        row += CellMap.DY0[i];
                    }
                    else
                    {
                        col += CellMap.DX1[i];
                        row += CellMap.DY1[i];
                    }
                    if(sum >= CellMap.ERASABLE_LIMIT) return true;
                    cell = this.getCell(col, row);
                }
                col = a.col;
                row = a.row;
                cell = this.getCell(col, row);
                sum --;
                while((cell != null) && (cell.type == a.type))
                {
                    sum ++;
                    if((row&1) == 0)
                    {
                        col += CellMap.DX0[i + CellMap.STEP/2];
                        row += CellMap.DY0[i + CellMap.STEP/2];
                    }
                    else
                    {
                        col += CellMap.DX1[i + CellMap.STEP/2];
                        row += CellMap.DY1[i + CellMap.STEP/2];
                    }
                    if(sum >= CellMap.ERASABLE_LIMIT) return true;
                    cell = this.getCell(col, row);
                }
            }
            return false;
        }

        public function isSwappable(a:Cell, b:Cell):Boolean
        {
            if((a == null) || (b == null)) return false;
            var swappable:Boolean = false;
            if(this.isAdjacent(a, b))
            {
                this.swapCells(a, b);
                if(this.isErasable(a)) swappable = true;
                else if(this.isErasable(b)) swappable = true;
                this.swapCells(a, b);
            }
            return swappable;
        }

// SwapFunction
        public function swapCells(a:Cell, b:Cell):void
        {
            var temp:int = a.type;
            a.type = b.type;
            b.type = temp;
        }

        public function eraseCell(a:Cell):int
        {
            var eraseList:Array = new Array();
            var sum:int = 0;
            if((a.type != Cell.TYPE_EMPTY) && isErasable(a))
            {
                eraseList.push(a);
                a.erased = true;
            }
            var l:int = 0;
            while(l < eraseList.length)
            {
                for(var i:int=0; i<CellMap.STEP; i++)
                {
                    var col:int = eraseList[l].col;
                    var row:int = eraseList[l].row;
                    if((row&1) == 0)
                    {
                        col += CellMap.DX0[i];
                        row += CellMap.DY0[i];
                    }
                    else
                    {
                        col += CellMap.DX1[i];
                        row += CellMap.DY1[i];
                    }
                    var cell:Cell = this.getCell(col, row);
                    if(this.isErasable(cell) && cell.type == a.type)
                    {
                        if(!cell.erased)
                        {
                            cell.erased = true;
                            eraseList.push(cell);
                        }
                    }
                }
                l ++;
            }
            if(eraseList.length >= CellMap.ERASABLE_LIMIT)
            {
                sum = eraseList.length;
                this.score += sum - ERASABLE_LIMIT + 1;
                this.comb.text = this.score.toString();
                trace("EraseCells = " + sum.toString());
                trace("Info: comb=" + score.toString());
                //this.gameVo.score += this.gameVo.scoreToEgg[this.gameVo.level - 1] * (sum - CellMap.ERASABLE_LIMIT + 1);
                this.mission.complete(a.type, sum);
            }
            for(i=eraseList.length-1; i>=0; i--)
            {
                eraseList[i].erase();
                eraseList.pop();
            }
            return sum;
        }

        public function eraseCells():void
        {
            var sum:int = 0;
            for(var row:int=0; row<this.map.length; row++)
            {
                for(var col:int=0; col<this.map[row].length; col++)
                {
                    sum += this.eraseCell(this.map[row][col]);
                }
            }
            trace("Event: Sum eraseCells = " + sum.toString());
            if(sum > 0)
            {
                this.erasingCellNum += sum;
                this.scalingCellNum += sum;
            }
            else
            {
                if(this.gameVo.state == Constants.GAME_STATE.RUNNING)
                {
                    trace("==================");
                    this.gameVo.score += this.gameVo.scoreToEgg[this.gameVo.level - 1] * this.score;
                    this.score = 0;
                    this.comb.text = this.score.toString();
                    Cell.LOCKED_ALL = false;
                }
            }
        }

        public function dropCells():int
        {
            this.state = CellMap.STATE_DROPPING;
            var sum:int = 0;
            for(var row:int=this.map.length-1; row>0; row--)
            {
                for(var col:int=0; col<this.map[row].length; col++)
                {
                    if(this.map[row][col].type == Cell.TYPE_EMPTY)
                    {
                        sum ++;
                        var c:int = this.map[row][col].col;
                        var r:int = this.map[row][col].row;
                        var cell:Cell = null;
                        var n:int = CellMap.STEP - 1;
                        if((row&1) == 0)
                        {
                            c += CellMap.DX0[0];
                            r += CellMap.DY0[0];
                        }
                        else
                        {
                            c += CellMap.DX1[n];
                            r += CellMap.DY1[n];
                        }
                        cell = this.getCell(c, r);
                        if((cell != null) &&
                           (cell.type != Cell.TYPE_EMPTY) &&
                           (cell.state != Cell.STATE_MOVING))
                        {
                            this.movingCellNum += 2;
                            cell.moveTo(this.map[row][col]);
                            this.map[row][col].moveTo(cell);
                            continue;
                        }

                        c = this.map[row][col].col;
                        r = this.map[row][col].row;
                        if((row&1) == 0)
                        {
                            c += CellMap.DX0[n];
                            r += CellMap.DY0[n];
                        }
                        else
                        {
                            c += CellMap.DX1[0];
                            r += CellMap.DY1[0];
                        }
                        cell = this.getCell(c, r);
                        if((cell != null) &&
                           (cell.type != Cell.TYPE_EMPTY) &&
                           (cell.state != Cell.STATE_MOVING))
                        {
                            this.movingCellNum += 2;
                            cell.moveTo(this.map[row][col]);
                            this.map[row][col].moveTo(cell);
                            continue;
                        }
                    }
                }
            }
            //trace("Debug: dropCells = " + sum.toString());
            //trace("Debug: movingCellNum = " + this.movingCellNum.toString());
            return sum;
        }

        public function createCells():void
        {
            var sum:int = 0;
            for(var i:int=0; i<CellMap.COL; i++)
            {
                if(this.map[0][i].type == Cell.TYPE_EMPTY)
                {
                    this.map[0][i].type = Cell.TYPE_NUM * Math.random();
                    this.map[0][i].display();
                    sum ++;
                }
            }
            this.erasingCellNum -= sum;
            //trace("Log: createCells = " + sum.toString());
            //trace("Log: erasingCellNum = " + this.erasingCellNum.toString());
            if(this.erasingCellNum <= 0)
            {
                this.state = CellMap.STATE_NORMAL;
                this.eraseCells();
            }
            else
            {
                this.dropCells();
            }
        }

// MouseFunction
        private function mouseOver(e:MouseEvent):void
        {
            if((!Cell.LOCKED_ALL) && (Cell.SELECTED_CELL != null))
            {
                if(Cell.OVERED_CELL != null &&
                   Cell.OVERED_CELL != Cell.SELECTED_CELL)
                {
                    if(this.isSwappable(Cell.SELECTED_CELL,
                                        Cell.OVERED_CELL))
                    {
                        this.movingCellNum += 2;
                        this.state = CellMap.STATE_SWAPPING;
                        Cell.LOCKED_ALL = true;
                        Cell.SWAPPING = true;
                        Cell.SELECTED_CELL.moveTo(Cell.OVERED_CELL);
                        Cell.OVERED_CELL.moveTo(Cell.SELECTED_CELL);
                        Cell.SELECTED_CELL.state = Cell.STATE_NORMAL;
                        Cell.SELECTED_CELL.display();
                        Cell.SELECTED_CELL = null;
                    }
                }
            }
        }

        private function mouseOut(e:MouseEvent):void
        {
            if(Cell.OVERED_CELL != null &&
               Cell.OVERED_CELL.state != Cell.STATE_SELECTED)
            {
                Cell.OVERED_CELL.state = Cell.STATE_NORMAL;
                Cell.OVERED_CELL.display();
                Cell.OVERED_CELL = null;
            }
        }

        private function cellErased(e:Event):void
        {
            this.scalingCellNum --;
            //trace("Event: erased");
            if(this.scalingCellNum <= 0)
            {
                //trace("Event: erased all");
                this.createCells();
            }
        }

        private function cellMoved(e:Event):void
        {
            this.movingCellNum --;
            if(this.movingCellNum <= 0)
            {
                if(this.state == CellMap.STATE_SWAPPING)
                {
                    Cell.SWAPPING = false;
                    this.state = CellMap.STATE_NORMAL;
                    this.eraseCells();
                }
                else if(this.state == CellMap.STATE_DROPPING)
                {
                    this.createCells();
                }
            }
        }
    }
}
